#include "material.h"
#include <cassert>
#include <string>
#include <sstream>
#include <fstream>
#include <math.h>
#include "scene.h"
#include "brdf_phong.h"
#include "bsdf_mtl.h"
namespace gemren
{
	
	material_ptr material::default_material()
	{
		material_ptr mat = new bsdf_material();
		string prop = "bsdf";
		mat->set_string(prop, "diffuse");
		prop = "color";
		mat->set_string(prop, "0.5");
		return mat;
	}

	err_t bsdf_material::set_int(std::string& name, const int value)
	{
		return BSDF->set_int(name, value);
	}
	err_t bsdf_material::set_string(std::string& name, const std::string& value)
	{
		if(name == "bsdf")
		{
			if(value == "diffuse")
			{
				delete BSDF;
				BSDF = new brdf_diffuse();
			}
			else if(value == "transparent")
			{
				delete BSDF;
				BSDF = new bsdf_fresnel();
			}
			else if(value == "specular")
			{
				delete BSDF;
				BSDF = new brdf_specular();
			}
			else if(value == "phong")
			{
				delete BSDF;
				BSDF = new brdf_phong();
			}
			else if(value == "mtl")
			{
				delete BSDF;
				BSDF = new bsdf_mtl();
			}
			else return ERR_BAD_VAL;
			return ERR_OK_RESET_CAUSTICS;
		}
		if(name == "Tf")
		{
			display_color = spectrum_dynamic(value, 0, 1);
			color.copy_from_spectrum(display_color);
			return ERR_OK;
		}
		//else
		return BSDF->set_string(name, value);
	}
	
	err_t bsdf_material::set_float(std::string& name, const float value)
	{
		return BSDF->set_float(name, value);
	}
	err_t bsdf_material::set_bool(std::string& name, const bool value)
	{
		err_t res = material::set_my_bool(name, value);
		return res==ERR_OK? ERR_OK : BSDF->set_bool(name, value);
	}
	void material::interact(random* ran, const scene* sc, intersection* i)
	{
		if(inverted_normals)
			i->normal = -i->normal;
		//shade(ran, scene, i); 
		
		if(is_directly_lighted())
			direct_lighting(ran, sc, i);
		bounce(ran, sc, i);
	}
	void material::interact_fast(random* ran, const scene* sc, intersection* i)
	{
		if(inverted_normals)
			i->normal = -i->normal;
		//shade(ran, scene, i); 
		i->lighting_mode = LM_PRIMARY_LIGHTSOURCE | LM_CAUSTIC_BRDF;
		if(is_directly_lighted())
			direct_lighting(ran, sc, i);
		bounce_fast(ran, sc, i);
	}
	
	void bsdf_material::interact(random* ran, const scene* sc, intersection* i)
	{
		i->from_inside = embree::dot(i->normal, i->incoming) > 0;
		if(i->from_inside) i->dim_light( spectrum::pow(color, i->t));
		material::interact(ran,sc,i);
	}
	void bsdf_material::interact_fast(random* ran, const scene* sc, intersection* i)
	{
		i->from_inside = embree::dot(i->normal, i->incoming) > 0;
		if(i->from_inside) i->dim_light( spectrum::pow(color, i->t));
		material::interact_fast(ran,sc,i);
	}
	
	void bsdf_material::announce_properties(const std::string& prefix, property_announcer announcer)
	{		
		material::announce_properties(prefix, announcer);
		string sbsdf;
		if(dynamic_cast<bsdf_mtl*>(BSDF))
			sbsdf = "mtl";
		if(dynamic_cast<brdf_phong*>(BSDF))
			sbsdf = "phong";
		else if(dynamic_cast<brdf_diffuse*>(BSDF))
			sbsdf = "diffuse";
		else if(dynamic_cast<brdf_specular*>(BSDF))
			sbsdf = "specular";
		else if(dynamic_cast<bsdf_fresnel*>(BSDF))
			sbsdf = "transparent";
		announcer((prefix+".bsdf").c_str(), ANNOUNCED_STRING, sbsdf.c_str());
		
		if(BSDF->has_btdf()) announcer((prefix+".Tf").c_str(), ANNOUNCED_STRING, val_to_str(display_color).c_str());
		BSDF->announce_properties(prefix, announcer);
		
	}

	using namespace std;
	
	material* material::create_material(const char filename[])
	{
		ifstream f(filename);
		if(!f.good()) return new bsdf_material;
		string line, atom;
		refraction_s refr_ind;
		color_s color;
		embree::Col3f color3;
		material* mat = new bsdf_material;
		while(!f.eof())
		{
			getline(f,line);
			stringstream ls(line);
			ls>>atom;
			if(atom == "type")
			{
				ls>>atom;
				if(atom == "bsdf")
				{
					delete mat;
					mat = new bsdf_material;
				}
				else if(atom == "light")
				{
					delete mat;
					mat = new light_emitting_material;
				}
			}
			else
			{
				if(!mat) mat = new bsdf_material;
				mat->set_prop(ls.seekg(0));
			}
		}
		f.close();
		return mat;
	
	}
		
	void bsdf_material::shade(random* ran, const scene* sc, intersection* i)
	{
		if(surface_shader) surface_shader->shade(ran, sc, i);
	}
	void bsdf_material::direct_lighting(random* ran, const scene* sc, intersection* i)
	{
		sc->direct_lighting(ran, i); 
	}

	void bsdf_material::bounce(random* ran, const scene* sc, intersection* i)
	{
		this->BSDF->bounce(ran, sc, i);
		if(BSDF->is_directly_lighted())
		{
			i->mis_brdf_importance = BSDF->pdf(-i->incoming, i->outcoming, i->normal);
			i->rayorigin = ro_diffuse;
			if(i->diffuse_reflection) //this is second diffuse reflection 
				i->lighting_mode = (i->lighting_mode & LM_PRIMARY) | LM_CAUSTIC_BRDF;
			else
				i->diffuse_reflection = true;
		}
		if(BSDF->is_light_transmitting() && i->rayorigin == ro_diffuse)
		{
			i->rayorigin = ro_transmitted_diffuse;
			if( (i->lighting_mode & LM_CAUSTIC_MIS) )
			{
				sc->finish_caustic_mis_coef(i);
			}
			//this is now done in ^^
			//else i->mis_brdf_importance /= (i->t*i->t); //this will finish computation of th coefficient started at the diffuse reflection -- we needed distance to the transmitter
		}
		//else bsdf is transmitting, importance is the same
	}
	void bsdf_material::bounce_fast(random* ran, const scene* sc, intersection* i)
	{
		this->BSDF->bounce_fast(ran, sc, i);
	}

}